import { Col } from "../core/data_ext";
import SoundManager from "../core/sound_manager";
import { Subject } from "../core/subject";
import { Sync, Uid, Waiter } from "../core/utils";
const { ccclass, property } = cc._decorator;

export class AniStateSyncer {
    subject: Subject = new Subject();
    uid = Uid.getUid();
    syncer: Waiter<void> = new Waiter<void>();
    state: cc.AnimationState = null;
};

export class AniPlayerChannel {
    ani: cc.Animation = null;
};
@ccclass
export default class AniPlayer extends cc.Component {
    @property
    playOnStart = true;
    @property({
        visible() {
            return this.playOnStart;
        },
    })
    delay = 0;

    @property({
        visible() {
            return this.playOnStart;
        },
    })
    playAniName = "";

    _ani: cc.Animation = null;
    @property(cc.Animation)
    get ani() {
        if (!this._ani) {
            this._ani = this.getComponent(cc.Animation);
        }
        return this._ani
    }

    channels: AniPlayerChannel[] = [];

    getChannel(ind: number) {
        if (!this.channels[ind]) {

            let channel = new AniPlayerChannel();
            channel.ani = this.addComponent(cc.Animation);

            this.ani.getClips().forEach(clip => {
                channel.ani.addClip(clip);
            });
            channel.ani.defaultClip = this.ani.defaultClip;
            channel.ani.playOnLoad = false;
            this.channels[ind] = channel;
        }

        return this.channels[ind];
    }

    onLoad() {
        if (!this.ani) {
            cc.log("AniPlayer: cant find cc.Animation");
            this.destroy();
            return;
        }
        this.channels[0] = new AniPlayerChannel();
        this.channels[0].ani = this.ani;
    }

    async start() {
        if (this.playOnStart) {
            if (this.delay > 0) {
                await new Promise<void>(ok => {
                    cc.tween(this.node).delay(this.delay).call(ok).start();
                });
            }
            this.channels[0].ani.play(this.playAniName);
        }
    }
    aniDestroy() {
        this.node.destroy();
    }
    playClip(name) {
        SoundManager.ins.playClip(name);
    }
    playClipEx(name, loop, volDetail, isFade) {
        SoundManager.ins.playClip(name, loop, volDetail);
    }
    stopClip(name) {
        SoundManager.ins.stopClip(name);
    }
    stopClipEx(name, isFade) {
        SoundManager.ins.stopClip(name);
    }
    emitAniMsg(msg: string) {
        console.log(this, msg);
        this.asList.forEach((as) => {
            as.subject.emit("ani-msg", msg);
        });
    }
    asList: AniStateSyncer[] = [];
    _prevAniName = "";
    _aniState: cc.AnimationState = null;
    playState(channelInd: number, name, wrap: cc.WrapMode = cc.WrapMode.Loop, startTime: number = 0) {
        let channel = this.getChannel(channelInd);
        if (this._prevAniName === name) {
            return;
        }

        if (this._aniState) {
            this._aniState.stop();
            this._aniState = null;
        }
        // channel.ani.stop(this._prevAniName);
        this._prevAniName = name;
        let state = channel.ani.getAnimationState(name);
        if (!state) {
            return;
        }
        state = channel.ani.playAdditive(name, startTime);
        state.wrapMode = wrap;
        this._aniState = state;
    }
    async play(channelInd: number, name: string, wrap: cc.WrapMode = cc.WrapMode.Normal, isAdditive = true, startTime: number = 0) {
        let channel = this.getChannel(channelInd);
        let state = channel.ani.getAnimationState(name);
        if (!state) {
            return;
        }
        //如果在播放，必须先停止之前的，不然stop消息会立即触发。
        if (state.isPlaying) {
            state.stop();
            await Sync.NextUpdate();
        }

        if (isAdditive) {
            channel.ani.playAdditive(name, startTime);
        }
        else {
            channel.ani.play(name, startTime);
        }
        let ass = new AniStateSyncer();
        state.wrapMode = wrap;
        let cb = (evtName: string, state: cc.AnimationState) => {
            console.log(`动画状态:${evtName}`);
            if (state.clip.name === name) {
                channel.ani.off(cc.Animation.EventType.FINISHED, cb, this);
                channel.ani.off(cc.Animation.EventType.STOP, cb, this);
                ass.syncer.finish();
                ass.syncer = null;
                ass.state = null;
                cc.js.array.remove(this.asList, ass);
            }
        };
        channel.ani.on(cc.Animation.EventType.FINISHED, cb, this);
        channel.ani.on(cc.Animation.EventType.STOP, cb, this);
        ass.state = state;
        ass.syncer = ass.syncer;
        this.asList.push(ass);
        return ass;
    }
};